package com.example.demo.component.k8s;

import com.example.demo.component.k8s.bean.deploy;
import com.example.demo.component.k8s.bean.pod;
import com.example.demo.component.others.ThreadPoolFactory;
import io.kubernetes.client.Exec;
import io.kubernetes.client.custom.V1Patch;
import io.kubernetes.client.extended.kubectl.Kubectl;
import io.kubernetes.client.extended.kubectl.exception.KubectlException;
import io.kubernetes.client.openapi.ApiClient;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.Configuration;
import io.kubernetes.client.openapi.apis.CoreV1Api;
import io.kubernetes.client.openapi.models.*;
import io.kubernetes.client.util.ClientBuilder;
import io.kubernetes.client.util.KubeConfig;
import io.kubernetes.client.util.Streams;
import io.kubernetes.client.util.Yaml;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * @ClassName k8s_client
 * @Description
 * @date 2022/11/8 10:33
 * @Author yanceysong
 * @Version 1.0
 */
//k8s GitHub 文档：https://github.com/fabric8io/kubernetes-client#creating-a-client
//    https://github.com/kubernetes-client/java/
//    api introduction ：https://kubernetes-client.github.io/javascript/classes/corev1api.corev1api-1.html
//    扩展kubectl：https://github.com/kubernetes-client/java/blob/master/docs/kubectl-equivalence-in-java.md 使用场景简单所以用的这个
public class k8s_client {
    private final String DEFAULT_NAMESPACE = "default";
    private ApiClient client = null;
    private CoreV1Api api = null;

    /**
     * 构造方法
     *
     * @param kube_conf_path 传入kube配置文件路径
     */
    public k8s_client(String kube_conf_path) {
        try {
            client = ClientBuilder.kubeconfig(KubeConfig.loadKubeConfig(new FileReader(kube_conf_path))).build();
            Configuration.setDefaultApiClient(client);
            api = new CoreV1Api();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    protected static void copyAsync(InputStream in, OutputStream out) {
        ThreadPoolFactory.getThreadPool().submit(() -> {
            try {
                Streams.copy(in, out);
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        });
    }

    /**
     * 获取特定命名空间某个pod的日志
     *
     * @param pod_name       pod名字
     * @param namespace      指定的命名空间
     * @param container_name 容器名称
     * @return 日志
     */
    public String get_logs(String pod_name, String container_name, String namespace) {
        try {
            InputStream log_result = Kubectl.log().namespace(namespace).name(pod_name).container(container_name).execute();
            return k8s_client_util.stream2String(log_result);
        } catch (KubectlException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 在指定的命名空间根据yaml生成configmap
     *
     * @param yaml_path yaml路径
     * @param namespace 命名空间
     */
    public void create_configMap(String yaml_path, String namespace) {
        try {
            V1ConfigMap v1ConfigMap = Yaml.loadAs(new File(yaml_path), V1ConfigMap.class);
            Kubectl.apply(V1ConfigMap.class).namespace(namespace).resource(v1ConfigMap).execute();
        } catch (IOException | KubectlException e) {
            e.printStackTrace();
        }
    }

    /**
     * 在默认的命名空间根据yaml生成configmap
     *
     * @param yaml_path yaml路径
     */
    public void create_configMap(String yaml_path) {
        create_configMap(yaml_path, DEFAULT_NAMESPACE);
    }

    /**
     * 获取默认命名空间某个pod的日志
     *
     * @param pod_name pod名字
     * @return 日志
     */
    public String get_logs(String pod_name, String container_name) {
        return get_logs(pod_name, container_name, DEFAULT_NAMESPACE);
    }

    /**
     * 在pod容器内执行命令
     *
     * @param pod       pod名字
     * @param namespace 命名空间
     * @param command   指令
     * @return 命令的执行结果
     */
    public String exec(pod pod, String namespace, String command) {
        final String[] commands = {"sh", "-c", command};
        V1Pod pod1 = new V1Pod().metadata(new V1ObjectMeta().name(pod.getName()).namespace(namespace));
        Exec exec = new Exec(client);
        try {
            Process proc = exec.exec(pod1, commands, pod.getContainer_name(), false, false);
            InputStream inputStream = proc.getInputStream();
            proc.waitFor();
            byte[] bytes;
            bytes = new byte[inputStream.available()];
            inputStream.read(bytes);
            inputStream.close();
            proc.destroy();
            return new String(bytes, StandardCharsets.UTF_8);
        } catch (ApiException | IOException | InterruptedException ex) {
            ex.printStackTrace();
        }
        return "";
    }

    /**
     * 在pod容器内执行命令
     *
     * @param pod     pod名字
     * @param command 指令
     * @return 命令的执行结果
     */
    public String exec(pod pod, String command) {
        return exec(pod, DEFAULT_NAMESPACE, command);
    }

    /**
     * 根据yaml在指定的命名空间创建deploy
     *
     * @param yaml_path yaml路径
     * @param namespace 命名空间
     */
    public void create_deploy(String yaml_path, String namespace) {
        try {
            V1Deployment v1Deployment = Yaml.loadAs(new File(yaml_path), V1Deployment.class);
            Kubectl.apply(V1Deployment.class).namespace(namespace).resource(v1Deployment).execute();
        } catch (KubectlException | IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据yaml在默认的命名空间创建deploy
     *
     * @param yaml_path yaml路径
     */
    public void create_deploy(String yaml_path) {
        create_deploy(yaml_path, DEFAULT_NAMESPACE);
    }

    /**
     * 在指定命名空间根据yaml删除一个deploy
     *
     * @param yaml_path yaml路径
     * @param namespace 命名空间
     */
    public void delete_deploy(String yaml_path, String namespace) {
        try {
            V1Deployment v1Deployment = Yaml.loadAs(new File(yaml_path), V1Deployment.class);
            Kubectl.delete(V1Deployment.class).namespace(namespace).name(v1Deployment.getMetadata().getName()).execute();
        } catch (KubectlException | IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 在默认的命名空间根据yaml删除一个deploy
     *
     * @param yaml_path yaml路径
     */
    public void delete_deploy(String yaml_path) {
        delete_deploy(yaml_path, DEFAULT_NAMESPACE);
    }

    /**
     * 给某个deploy扩展副本
     *
     * @param deploy_name deploy名字
     * @param namespace   指定的命名空间
     * @param number      副本数量
     */
    public void scale(String deploy_name, String namespace, Integer number) {
        try {
//            Kubectl.scale(V1Deployment.class).namespace(namespace).name(deploy_name).replicas(number).execute();
            Kubectl.patch(V1Deployment.class).name(deploy_name).namespace(namespace).patchType(V1Patch.PATCH_FORMAT_JSON_PATCH).patchContent(new V1Patch(String.format("[{\"op\":\"replace\",\"path\":\"/spec/replicas\",\"value\":%d}]", number))).execute();
        } catch (KubectlException e) {
            e.printStackTrace();
        }
    }

    public void drain_node(String node_name) {
        try {
            Kubectl.drain().name(node_name).execute();
        } catch (KubectlException e) {
            e.printStackTrace();
        }
    }

    /**
     * 给默认命名空间的某个deploy扩展副本
     *
     * @param deploy_name deploy名字
     * @param number      副本数量
     */
    public void scale(String deploy_name, Integer number) {
        scale(deploy_name, DEFAULT_NAMESPACE, number);
    }

    /**
     * 获取集群下面所有的命名空间
     *
     * @return 命名空间集合
     */
    public List<String> get_namespace() {
        try {
            ArrayList<String> result_list = new ArrayList<>();
            V1NamespaceList result = api.listNamespace(null, null, null, null, null, null, null, null, null, null);
            result.getItems().forEach(v1Namespace -> result_list.add(Objects.requireNonNull(v1Namespace.getMetadata()).getName()));
            return result_list;
        } catch (ApiException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取集群下面所有的node节点
     *
     * @return node节点集合
     */
    public List<String> get_node() {
        try {
            ArrayList<String> result_list = new ArrayList<>();
            V1NodeList result = api.listNode(null, null, null, null, null, null, null, null, null, null);
            result.getItems().forEach(V1Node -> result_list.add(Objects.requireNonNull(V1Node.getMetadata()).getName()));
            return result_list;
        } catch (ApiException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取特定命名空间下service集合
     *
     * @return 特定namespace的service集合
     */
    public List<String> get_service(String namespace) {
        try {
            ArrayList<String> result_list = new ArrayList<>();
            V1ServiceList result = api.listNamespacedService(namespace, null, null, null, null, null, null, null, null, null, null);
            result.getItems().forEach(V1Service -> result_list.add(Objects.requireNonNull(V1Service.getMetadata()).getName()));
            return result_list;
        } catch (ApiException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取default命名空间下service集合,
     *
     * @return 特定namespace的service集合
     */
    public List<String> get_service() {
        return get_service(DEFAULT_NAMESPACE);
    }

    /**
     * 获取某个命名空间下的pod
     *
     * @param namespace 命名空间
     * @return pod名字集合
     */
    public List<pod> get_pods(String namespace) {
        try {
            ArrayList<pod> result_list = new ArrayList<>();
            V1PodList result = api.listNamespacedPod(namespace, null, null, null, null, null, null, null, null, null, null);
            result.getItems().forEach(V1Pod -> result_list.add(k8s_client_util.V1Pod2pod(V1Pod)));
            return result_list;
        } catch (ApiException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 获取默认命名空间下的pod
     *
     * @return pod名字集合
     */
    public List<pod> get_pods() {
        return get_pods(DEFAULT_NAMESPACE);
    }

    /**
     * 在指定命名空间根据删除一个deploy
     *
     * @param v1Deployment deploy 对象
     * @param namespace    命名空间
     */
    public void delete_deploy(V1Deployment v1Deployment, String namespace) {
        try {
            Kubectl.delete(V1Deployment.class).namespace(namespace).name(v1Deployment.getMetadata().getName()).execute();
        } catch (KubectlException e) {
            e.printStackTrace();
        }
    }

    /**
     * 在指定命名空间根据创建一个deploy
     *
     * @param v1Deployment deploy 对象
     * @param namespace    命名空间
     */
    public void create_deploy(V1Deployment v1Deployment, String namespace) {
        try {
            Kubectl.create(V1Deployment.class).namespace(namespace).resource(v1Deployment).execute();
        } catch (KubectlException e) {
            e.printStackTrace();
        }
    }

    /**
     * 更新一个部署容器为某一个版本
     *
     * @param deploy_name deploy的名字
     * @param image_name  镜像名
     * @param new_version 要更改的容器版本
     * @param namespace   命名空间
     */
    public void update_deploy(String deploy_name, String image_name, String new_version, String namespace) {
        try {
            String new_image = image_name + ":" + new_version;
            Kubectl.patch(V1Deployment.class).name(deploy_name).namespace(namespace).patchType(V1Patch.PATCH_FORMAT_JSON_PATCH).patchContent(new V1Patch("[{\"op\": \"replace\", \"path\": \"/spec/template/spec/containers/0/image\", \"value\":\"" + new_image + "\"}]")).execute();
//            V1Patch body = new V1Patch("[{\"op\": \"replace\", \"path\": \"/spec/containers/0/image\", \"value\":\"" + new_image + "\"}]");
//            String pretty = "true";
//            api.patchNamespacedReplicationController(deploy_name, namespace, body, pretty, null, null, true);
        } catch (KubectlException e) {
            e.printStackTrace();
        }
    }

    /**
     * 更新一个部署容器为某一个版本
     *
     * @param deploy_name deploy的名字
     * @param image_name  镜像名
     * @param new_version 要更改的容器版本
     */
    public void update_deploy(String deploy_name, String image_name, String new_version) {
        update_deploy(deploy_name, image_name, new_version, DEFAULT_NAMESPACE);
    }

    /**
     * 在默认命名空间根据删除一个deploy
     *
     * @param v1Deployment deploy 对象
     */
    public void delete_deploy(V1Deployment v1Deployment) {
        delete_deploy(v1Deployment, DEFAULT_NAMESPACE);
    }

    /**
     * 在默认命名空间根据创建一个deploy
     *
     * @param v1Deployment deploy 对象
     */
    public void create_deploy(V1Deployment v1Deployment) {
        create_deploy(v1Deployment, DEFAULT_NAMESPACE);
    }

    /**
     * 获取deploy
     *
     * @param name_space 命名空间
     * @return 集合
     */
    public List<deploy> get_deploy(String name_space) {
        ArrayList<deploy> result = new ArrayList<>();
        try {
            List<V1Deployment> deploys = Kubectl.get(V1Deployment.class).namespace(name_space).execute();
            for (V1Deployment deployment : deploys) {
                result.add(k8s_client_util.V1Deploy2deploy(deployment));
            }
        } catch (KubectlException e) {
            throw new RuntimeException(e);
        }
        return result;
    }

    /**
     * 获取deploy
     *
     * @return 集合
     */
    public List<deploy> get_deploy() {
        return get_deploy(DEFAULT_NAMESPACE);
    }

    /**
     * 在指定命名空间下创建一个pod
     *
     * @param pod        pod
     * @param name_space 命名空间
     */
    public void create_pod(V1Pod pod, String name_space) {
        try {
            Kubectl.create(V1Pod.class).resource(pod).namespace(name_space).execute();
        } catch (KubectlException e) {
            e.printStackTrace();
        }
    }

    /**
     * 在默认命名空间下创建一个pod
     *
     * @param pod pod
     */
    public void create_pod(V1Pod pod) {
        create_pod(pod, DEFAULT_NAMESPACE);
    }

    /**
     * 根据yaml创建service
     *
     * @param yaml_path yaml路径
     * @param namespace 指定namespace
     */
    public void create_service(String yaml_path, String namespace) {
        try {
            V1Service v1Service = Yaml.loadAs(new File(yaml_path), V1Service.class);
            Kubectl.apply(V1Service.class).resource(v1Service).namespace(namespace).execute();
        } catch (IOException | KubectlException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据yaml创建在默认命名空间service
     *
     * @param yaml_path yaml 路径
     */
    public void create_service(String yaml_path) {
        create_service(yaml_path, DEFAULT_NAMESPACE);
    }

    /**
     * 根据yaml删除service
     *
     * @param yaml_path yaml路径
     * @param namespace 命名空间
     * @return 删除结果
     */
    public void delete_service(String yaml_path, String namespace) {
        try {
            V1Service v1Service = Yaml.loadAs(new File(yaml_path), V1Service.class);
            Kubectl.delete(V1Service.class).name(v1Service.getMetadata().getName()).namespace(namespace).execute();
//            Yaml.addModelMap("v1", "Service", V1Service.class);
//            File file = new File(yaml_path);
//            V1Service yamlSvc = (V1Service) Yaml.load(file);
//            V1Status v1Status = api.deleteNamespacedService(Objects.requireNonNull(yamlSvc.getMetadata()).getName(), namespace, null, null, null, null, null, new V1DeleteOptions());
        } catch (IOException | KubectlException e) {
            e.printStackTrace();
        }
    }

    /**
     * 在默认命名空间根据yaml删除一个service
     *
     * @param yaml_path yaml路径
     */
    public void delete_service(String yaml_path) {
        delete_service(yaml_path, DEFAULT_NAMESPACE);
    }

    /**
     * 根据yaml删除一个pod
     *
     * @param pod_name  pod名字
     * @param namespace 命名空间
     */
    public void delete_pod(String pod_name, String namespace) {
        try {
            Kubectl.delete(V1Pod.class).name(pod_name).namespace(namespace).execute();
        } catch (KubectlException e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除默认命名空间下的pod
     *
     * @param pod_name pod名字
     */
    public void delete_pod(String pod_name) {
        delete_pod(pod_name, DEFAULT_NAMESPACE);
    }
}
